/*
 * Please leave this Copyright notice in your code if you use it
 * Written by Decebal Mihailescu [http://www.codeproject.com/script/articles/list_articles.asp?userid=634640]
 */
using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.Windows.Forms.Design;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Text;
using System.Linq;
using Microsoft.Win32;
using CommonUtils;
using System.Reflection;

//see http://vladsnotes.hrybok.com/CategoryView,category,MSI.aspx
//or http://devcity.net/Articles/339/6/article.aspx
namespace FreeTranslator
{
    using System.Text;
    using System.Configuration.Install;
    using System.Collections;
    using MyTextImport = Lazy<ITextImport, ITextImportMetaData>;

    [RunInstaller(true)]
    public partial class NativeInstaller : Installer
    {

        public NativeInstaller()
        {
            string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "LanguageTranslatorInstall.log");
            Trace.Listeners.Add(new TextWriterTraceListener(logPath));
            Trace.TraceInformation("NativeInstaller Started at {0}, Win ver = {1}, Crt dir: '{2}', CLR version ={3}, Is64BitProcess = {4}, Is64BitOperatingSystem = {5}",
                DateTime.Now, Environment.OSVersion.Version, Environment.CurrentDirectory, RuntimeEnvironment.GetSystemVersion(), Environment.Is64BitProcess, Environment.Is64BitOperatingSystem);
            InitializeComponent();
            Trace.TraceInformation("NativeInstaller initialized");
        }
        ITextImportController PlugInController
        {
            get
            {
                return MEFController.PlugInImportController;
            }
        }
        //[DllImport("mscoree.dll", EntryPoint = "GetCORSystemDirectory", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
        //private static extern int GetCORSystemDirectory([MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder Buffer, int BufferLength, ref int Length);

        struct Element { internal string file; internal string assembly;}
        internal static void ExecuteNGen(bool bUninstall)
        {
            // string mainOutput = this.Context.Parameters["AssemblyPath"];

            string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string folder = Path.GetDirectoryName(path) + "\\";
            Process proc = null;
            try
            {

                string ngenpath = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "ngen.exe");
                if (!File.Exists(ngenpath))
                    return;
                ProcessStartInfo si = null;
                if (bUninstall)
                {
                    si = new ProcessStartInfo(ngenpath, string.Format("uninstall \"{0}\"", Path.GetFileNameWithoutExtension(path)));
                }
                else
                {
                    si = new ProcessStartInfo(ngenpath, string.Format("install \"{0}\" /NoDependencies", path));
                }
                si.WorkingDirectory = Path.GetDirectoryName(path);
                si.WindowStyle = ProcessWindowStyle.Hidden;
                proc = Process.Start(si);
                proc.WaitForExit(30000);//30 seconds
                //if (proc.ExitCode != 0)
                //    MessageBox.Show(ngenpath + " " + si.Arguments + " failed with code " + proc.ExitCode.ToString());
                Trace.TraceInformation(ngenpath + " " + si.Arguments + " failed with code " + proc.ExitCode.ToString());

                Element[] collection = new Element[] { new Element { file = "CommonUtils.dll", assembly = "CommonUtils" }, new Element { file = "WebLanguageUtils.dll", assembly = "WebLanguageUtils" }, new Element { file = "TextExtractors.dll", assembly = "TextExtractors" }, new Element { file = "itextsharp.dll", assembly = "itextsharp" }, new Element { file = "SubTitleConvertors.dll", assembly = "SubTitleConvertors" } };

                foreach (Element item in collection)
                {
                    si.Arguments = bUninstall ? string.Format("uninstall \"{0}\"", item.assembly) : string.Format("install \"{0}\" /NoDependencies", Path.Combine(folder, item.file));
                    proc = Process.Start(si);
                    proc.WaitForExit(30000);
                    //if (proc.ExitCode != 0)
                    Trace.TraceInformation(ngenpath + " " + si.Arguments + " returned with code " + proc.ExitCode.ToString());
                }

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                throw new InstallException(ex.Message);
            }
            finally
            {
                Trace.Flush();
            }
        }
        private bool IsUpgrade
        {
            get
            {
                string oldPC = this.Context.Parameters["OldProductCode"];
                Trace.TraceInformation("OldProductCode = '{0}' IsUpgrade =  {1}", oldPC, !string.IsNullOrEmpty(oldPC));
                return !string.IsNullOrEmpty(oldPC);
            }
        }


        [SecurityPermission(SecurityAction.Demand)]
        public override void Uninstall(System.Collections.IDictionary savedState)
        {
            // Get the custom parameters from the saved state.
            //ShowContextAndState("Uninstall", savedState);
            base.Uninstall(savedState);
            ShowContextAndState("Uninstall", savedState);
            NativeWindow _wnd = new NativeWindow();
            try
            {
                PersistedSharedData.Version = "";
                _wnd.AssignHandle(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
                ExecuteNGen(true);
                RunSetup("/u");

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
            }
            finally
            {
                Trace.Flush();
                _wnd.ReleaseHandle();
            }
        }

        /// <summary>
        /// To cause this method to be invoked, I added the primary project output to the 
        /// setup project's custom actions, under the "Uninstall" folder.
        /// </summary>
        /// <param name="savedState">An IDictionary object that will contain objects that were set as 
        /// part of the installation process.</param>
        [SecurityPermission(SecurityAction.Demand)]
        public override void Install(System.Collections.IDictionary stateSaver)
        {

            try
            {
                base.Install(stateSaver);
#if(DEBUG)
                stateSaver.Add("TargetDir", Context.Parameters["TargetDir"].ToString());
                stateSaver.Add("ProductVersion", Context.Parameters["ProductVersion"].ToString());
                stateSaver.Add("UpgradeCode", Context.Parameters["UpgradeCode"].ToString());
                stateSaver.Add("VSDFrameworkVersion", Context.Parameters["VSDFrameworkVersion"].ToString());
                stateSaver.Add("PrevVersions", Context.Parameters["PrevVersions"].ToString());//PREVIOUSVERSIONSINSTALLED
#endif
                ShowContextAndState("AfterInstall", stateSaver);
            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
            }
            finally
            {
                Trace.Flush();

            }


        }

        [SecurityPermission(SecurityAction.Demand)]
        public override void Rollback(System.Collections.IDictionary savedState)
        {
            base.Rollback(savedState);
            if (!string.IsNullOrEmpty(_originalVersion))
                PersistedSharedData.Version = _originalVersion;
            ShowContextAndState("Rollback", savedState);
            base.Rollback(savedState);
        }

        static internal readonly string _shellRegMenuName = Assembly.GetExecutingAssembly().GetName().Name + "MenuEntry";//"FLTMenuEntry";//
        string _originalVersion;
        public override void Commit(System.Collections.IDictionary savedState)
        {
            base.Commit(savedState);
            _originalVersion = PersistedSharedData.Version;
            base.Commit(savedState);
            ShowContextAndState("commit", savedState);
            try
            {
                PersistedSharedData.Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
                ExecuteNGen(false);
                PersistedSharedData.DefaultPlugInDirectory = System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location);
                RunSetup("/i");

            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
            }
            finally
            {
                Trace.Flush();

            }

        }

        internal static int RunSetup(string arg)
        {
            try
            {
                string fullPath = System.Reflection.Assembly.GetExecutingAssembly().Location; //@"C:\SandBox\Translator\bin\debug\freeLanguageTranslator.exe";
                ProcessStartInfo si = new ProcessStartInfo(fullPath);
                si.WorkingDirectory = Path.GetDirectoryName(fullPath);
                si.WindowStyle = ProcessWindowStyle.Normal;
                si.Arguments = arg;
                Process proc = Process.Start(si);
                proc.WaitForExit(20000);//20 seconds
                //Check to see if the process is still running.
                if (proc.HasExited == false)
                {
                    //Process is still running.
                    //Test to see if the process is hung up.
                    if (proc.Responding)
                        //Process was responding; close the main window.
                        proc.CloseMainWindow();
                    else
                        //Process was not responding; force the process to close.
                        proc.Kill();
                    return -1;
                }
                return proc.ExitCode;
            }
            catch (Exception ex)
            {
                Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                return -1;
            }
            finally
            {
                Trace.Flush();

            }
        }


        [ConditionalAttribute("DEBUG")]
        private void ShowContextAndState(string action, System.Collections.IDictionary savedState)
        {
            NativeWindow _wnd = new NativeWindow();
            try
            {
                _wnd.AssignHandle(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
                StringBuilder sb = new StringBuilder(200);

                sb.Append("savedState count = " + savedState.Count.ToString());
                sb.Append("\tsavedState keys = " + savedState.Keys.Count.ToString());
                sb.AppendLine("\tsavedState values = " + savedState.Values.Count.ToString());

                foreach (string k in savedState.Keys)
                {
                    sb.AppendLine("savedState key[\"" + k + "\"]= " + savedState[k].ToString());
                }

                writeContextSB(sb);

                //MessageBox.Show(_wnd, sb.ToString(), action, MessageBoxButtons.OK, MessageBoxIcon.Information);
                Trace.TraceInformation("ContextAndState {0}",sb.ToString());
                sb.Clear();

            }
            catch (Exception ex)
            {
Trace.TraceError("'{0}' failed to start because {1}", ex.StackTrace, ex.Message);
                throw new InstallException(action + " failed", ex);
            }
            finally
            {
                Trace.Flush();
                _wnd.ReleaseHandle();
            }
        }
        private void writeContextSB(StringBuilder sb)
        {
            sb.Append("Context Parameters:");
            sb.Append("\tCount = " + Context.Parameters.Count.ToString());
            sb.Append("\tKeys = " + Context.Parameters.Keys.Count.ToString());
            sb.AppendLine("\tValues = " + Context.Parameters.Values.Count.ToString());

            foreach (string k in Context.Parameters.Keys)
            {
                sb.AppendLine("ContextKey [\"" + k + "\"]=" + Context.Parameters[k].ToString());
            }
        }
    }
}